﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using LightCAD.MathLib;

namespace LightCAD.Core
{
    public static class Curve2dUtils
    {
        public static Curve2d Create(Curve2dType type)
        {
            switch (type)
            {
                case Curve2dType.Point2d:
                    return new Point2d();
                case Curve2dType.Line2d:
                    return new Line2d();
                case Curve2dType.Rect2d:
                    return new Rect2d();
                case Curve2dType.Arc2d:
                    return new Arc2d();
                case Curve2dType.Circle2d:
                    return new Circle2d();
                case Curve2dType.Spline2d:
                    return new Spline2d();
                case Curve2dType.Polygon2d:
                    return new Polygon2d();
                case Curve2dType.Polyline2d:
                    return new Polyline2d();
                case Curve2dType.Path2d:
                    return new Path2d();
            }
            return null;
        }

        public static void ReadProperties(ref JsonElement jele, Curve2d curve)
        {
            if (jele.TryGetProperty(nameof(curve.Name), out JsonElement nameProp))
                curve.Name = nameProp.GetString();

            switch (curve.Type)
            {
                case Curve2dType.Point2d:
                    {
                        var point = curve as Point2d;
                        point.XY = jele.ReadVector2dProperty(nameof(point.XY));
                        break;
                    }
                case Curve2dType.Line2d:
                    {
                        var line = curve as Line2d;
                        line.Start = jele.ReadVector2dProperty(nameof(line.Start));
                        line.End = jele.ReadVector2dProperty(nameof(line.End));
                        break;
                    }
                case Curve2dType.Rect2d:
                    {
                        var rect = curve as Rect2d;
                        rect.Min = jele.ReadVector2dProperty(nameof(rect.Min));
                        rect.Max = jele.ReadVector2dProperty(nameof(rect.Max));
                        break;
                    }
                case Curve2dType.Arc2d:
                    {
                        var arc = curve as Arc2d;
                        arc.Center = jele.ReadVector2dProperty(nameof(arc.Center));
                        arc.Radius = jele.ReadDoubleProperty(nameof(arc.Radius));
                        arc.StartAngle = jele.ReadDoubleProperty(nameof(arc.StartAngle));
                        arc.EndAngle = jele.ReadDoubleProperty(nameof(arc.EndAngle));
                        var isClockwise = jele.ReadBoolProperty(nameof(arc.IsClockwise));
                        arc.IsClockwise = isClockwise.HasValue && isClockwise.Value;
                        break;
                    }
                case Curve2dType.Circle2d:
                    {
                        var cir = curve as Circle2d;
                        cir.Center = jele.ReadVector2dProperty(nameof(cir.Center));
                        cir.Radius = jele.ReadDoubleProperty(nameof(cir.Radius));
                        break;
                    }
                case Curve2dType.Spline2d:
                    {
                        break;
                    }
                case Curve2dType.Polygon2d:
                    {
                        var polygon = curve as Polygon2d;
                        polygon.Points = jele.ReadListProperty<Vector2>(nameof(polygon.Points)).ToArray();
                        break;
                    }
                case Curve2dType.Polyline2d:
                    {
                        var polyline = curve as Polyline2d;
                        polyline.Points = jele.ReadListProperty<Vector2>(nameof(polyline.Points)).ToArray();
                        break;
                    }
                case Curve2dType.Path2d:
                    {
                        var path = curve as Path2d;
                        path.Curves = jele.ReadListProperty<Curve2d>(nameof(path.Curves)).ToArray();
                        break;
                    }
            }
        }

        public static void WriteProperties(Utf8JsonWriter writer, Curve2d curve, JsonSerializerOptions soptions)
        {
            if (!string.IsNullOrEmpty(curve.Name))
                writer.WriteStringProperty(nameof(curve.Name), curve.Name);

            switch (curve.Type)
            {
                case Curve2dType.Point2d:
                    {
                        var point = curve as Point2d;
                        writer.WriteVector2dProperty(nameof(point.XY), point.XY);
                        break;
                    }
                case Curve2dType.Line2d:
                    {
                        var line = curve as Line2d;
                        writer.WriteVector2dProperty(nameof(line.Start), line.Start);
                        writer.WriteVector2dProperty(nameof(line.End), line.End);
                        break;
                    }
                case Curve2dType.Rect2d:
                    {
                        var rect = curve as Rect2d;
                        writer.WriteVector2dProperty(nameof(rect.Min), rect.Min);
                        writer.WriteVector2dProperty(nameof(rect.Max), rect.Max);
                        break;
                    }
                case Curve2dType.Arc2d:
                    {
                        var arc = curve as Arc2d;
                        writer.WriteVector2dProperty(nameof(arc.Center), arc.Center);
                        writer.WriteNumberProperty(nameof(arc.Radius), arc.Radius);
                        writer.WriteNumberProperty(nameof(arc.StartAngle), arc.StartAngle);
                        writer.WriteNumberProperty(nameof(arc.EndAngle), arc.EndAngle);
                        writer.WriteBoolean(nameof(arc.IsClockwise), arc.IsClockwise);
                        break;
                    }
                case Curve2dType.Circle2d:
                    {
                        var cir = curve as Circle2d;
                        writer.WriteVector2dProperty(nameof(cir.Center), cir.Center);
                        writer.WriteNumberProperty(nameof(cir.Radius), cir.Radius);
                        break;
                    }
                case Curve2dType.Spline2d:
                    {
                        break;
                    }
                case Curve2dType.Polygon2d:
                    {
                        var polygon = curve as Polygon2d;
                        writer.WriteCollectionProperty<Vector2>(nameof(polygon.Points), polygon.Points, soptions);
                        break;
                    }
                case Curve2dType.Polyline2d:
                    {
                        var polyline = curve as Polyline2d;
                        writer.WriteCollectionProperty<Vector2>(nameof(polyline.Points), polyline.Points, soptions);
                        break;
                    }
                case Curve2dType.Path2d:
                    {
                        var path = curve as Path2d;
                        writer.WriteCollectionProperty<Curve2d>(nameof(path.Curves), path.Curves, soptions, (w, c) => w.WriteCurve2dObject(c, soptions));
                        break;
                    }
            }
        }
        public static Curve2d ApplyMatrix(this Curve2d curve ,Matrix3 matrix) 
        {
            switch (curve.Type)
            {
                case Curve2dType.None:
                    break;
                case Curve2dType.Point2d:
                    return (curve as Point2d).ApplyMatrix(matrix);
                case Curve2dType.Line2d:
                    return (curve as Line2d).ApplyMatrix(matrix);
                case Curve2dType.Rect2d:
                    break;
                case Curve2dType.Arc2d:
                    return (curve as Arc2d).ApplyMatrix(matrix);
                case Curve2dType.Circle2d:
                    return (curve as Circle2d).ApplyMatrix(matrix);
                case Curve2dType.Spline2d:
                    break;
                case Curve2dType.Polyline2d:
                    break;
                case Curve2dType.Polygon2d:
                    break;
                case Curve2dType.Path2d:
                    return (curve as Path2d).ApplyMatrix(matrix);
                case Curve2dType.Ellipse2d:
                    return (curve as Ellipse2d).ApplyMatrix(matrix);
                default:
                    Debug.Assert(false,"未知的Curve类型");
                    break;
            }
            return curve;
        }
        public static Point2d ApplyMatrix(this Point2d point, Matrix3 matrix) 
        {
            point.XY.ApplyMatrix3(matrix);
            return point;
        }
        public static Line2d ApplayMatrix(this Line2d line, Matrix3 matrix) 
        {
            line.Start.ApplyMatrix3(matrix);
            line.End.ApplyMatrix3(matrix);
            return line;
        }
        public static Circle2d ApplayMatrix(this Circle2d circle, Matrix3 matrix)
        {
            circle.Center.ApplyMatrix3(matrix);
            var scale = Math.Abs( matrix.GetScaleVector().X);
            circle.Radius*=scale;
            return circle;
        }
        public static Arc2d ApplayMatrix(this Arc2d arc, Matrix3 matrix)
        {
            arc.Center.ApplyMatrix3(matrix);
            var scaleVec = matrix.GetScaleVector();
            var isMirror = Math.Sign(scaleVec.X) != Math.Sign(scaleVec.Y);
            var scale = Math.Abs(scaleVec.X);
            if (isMirror)
            {
                double divAngle = 0;
                if (scaleVec.X < 0)
                divAngle = 0;
                if (scaleVec.Y < 0)
                    divAngle = Utils.HalfPI;
                if (divAngle > MathUtil.Pi)
                    divAngle -= MathUtil.Pi;
                arc.StartAngle -= 2 * (arc.StartAngle - divAngle);
                arc.EndAngle -= 2 * (arc.EndAngle - divAngle);
                arc.IsClockwise=!arc.IsClockwise;
            }
            var rotation = matrix.GetAngle();
            arc.Radius *= scale;
            arc.StartAngle += rotation ;
            arc.EndAngle += rotation ;
            return arc;
        }
        public static Ellipse2d ApplayMatrix(this Ellipse2d ellipse, Matrix3 matrix) 
        {
            ellipse.Center.ApplyMatrix3(matrix);
            var scaleVec = matrix.GetScaleVector();
            var isMirror = Math.Sign(scaleVec.X) != Math.Sign(scaleVec.Y);
            var scale = Math.Abs(scaleVec.X);
            if (isMirror)
            {
                double divAngle = 0;
                if (scaleVec.X < 0)
                    divAngle = 0;
                if (scaleVec.Y < 0)
                    divAngle = Utils.HalfPI;
                if (divAngle > MathUtil.Pi)
                    divAngle -= MathUtil.Pi;
                ellipse.StartAngle -= 2 * (ellipse.StartAngle - 0);
                ellipse.EndAngle -= 2 * (ellipse.EndAngle - 0);
                ellipse.Rotation -= 2 * (ellipse.Rotation - divAngle);
                ellipse.IsClockwise = !ellipse.IsClockwise;
            }
            var rotation = matrix.GetAngle();
            ellipse.RadiusX *= scale;
            ellipse.RadiusY *= scale;
            ellipse.Rotation += rotation;
            return ellipse;
        }
        public static Path2d ApplayMatrix(this Path2d path, Matrix3 matrix) 
        {
            for (int i = 0; i < path.Curves.Length; i++)
            {
                var curve = path.Curves[i];
                curve?.ApplyMatrix(matrix);
            }
            return path;
        }
    }
}
